#include <stdio.h>
#include "main.h"

#define PAGE_SIZE	        1024	    //页大小
#define PAGE_NUM	        128		    //页数

/* 分区         大小        扇区        说明
bootloader      9K         0-8       引导程序
flag            1K         9         版本信息
app             54K        10-63     应用程序
download		54K		   64-127	 下载区
*/
#define BOOTLOADER_PAGE_NUM     (9)
#define VERSION_INFO_PAGE_NUM   (1)
#define APPLICATION_PAGE_NUM    (59)
#define DOWNLOAD_PAGE_NUM      	(59)

/* bootloader分区 */
#define BOOTLOADER_ADDR         (0x08000000U)
#define BOOTLOADER_PAGE_START   (0)
#define BOOTLOADER_FLASH_SIZE   (PAGE_SIZE * BOOTLOADER_PAGE_NUM)
/* 版本信息分区 */
#define VERSION_INFO_ADDR       (BOOTLOADER_ADDR + BOOTLOADER_FLASH_SIZE)
#define VERSION_INFO_PAGE_START (BOOTLOADER_PAGE_START + BOOTLOADER_PAGE_NUM)
#define VERSION_INFO_FLASH_SIZE (PAGE_SIZE * VERSION_INFO_PAGE_NUM)
/* 应用程序分区 */
#define APPLICATION_ADDR        (VERSION_INFO_ADDR + VERSION_INFO_FLASH_SIZE)
#define APPLICATION_PAGE_START  (VERSION_INFO_PAGE_START + VERSION_INFO_PAGE_NUM)
#define APPLICATION_FLASH_SIZE  (PAGE_SIZE * APPLICATION_PAGE_NUM)
/* 下载分区 */
#define DOWNLOAD_ADDR          	(APPLICATION_ADDR + APPLICATION_FLASH_SIZE)
#define DOWNLOAD_PAGE_START    	(APPLICATION_PAGE_START + APPLICATION_PAGE_NUM)
#define DOWNLOAD_FLASH_SIZE    	(PAGE_SIZE * DOWNLOAD_PAGE_NUM)

/* 版本信息 */
#define VERSION_INFO_UPDATE_ADDR	(VERSION_INFO_ADDR)
#define VERSION_INFO_FILESIZE_ADDR	(VERSION_INFO_ADDR + 4)	
/* 升级密钥 */
#define UPDATE_KEY              (0x6D78346B)

void (*jump2app)(void);
uint8_t isFirmwareUpdate(void);
void turn_to_app(void);
void install_firmware(void);
void version_update_flag_reset(void);
void fmc_erase_page(uint32_t pageAddr);
void fmc_program_page(uint32_t pageAddr, uint8_t *data);
void debug_uart_init(void);

int main(void)
{
	nvic_priority_group_set(NVIC_PRIGROUP_PRE4_SUB0);

	if(isFirmwareUpdate())
	{
		/* 安装新固件 */
		install_firmware();
		/* 清除升级标志 */
		version_update_flag_reset();
		/* 复位 */
		NVIC_SystemReset();
	}
	else
	{
		turn_to_app();
	}
	while(1)
	{
		
	}
}

uint8_t isFirmwareUpdate(void)
{
	uint32_t updateFlag = (*(__IO uint32_t *)VERSION_INFO_UPDATE_ADDR);
	/* 检查版本信息 */
	if(updateFlag == UPDATE_KEY)
	{
		return 1;
	}
	return 0;
}

void turn_to_app(void)
{
    /* 检查栈顶地址是否合法，也就是 APP 存放的起始地址是否合法 */
    if(((*(__IO uint32_t *)APPLICATION_ADDR) & 0x2FFE0000) == 0x20000000)
    {
        /* 获取APP程序起始地址，代码区第二个字（起始地址+4   Flash 位置存储的数据） */
        jump2app = (void (*)(void)) (*(volatile uint32_t *)(APPLICATION_ADDR + 4));
        /* 初始化堆栈指针（用户代码区的第一个字用于存放栈顶地址） */
        __set_MSP(*(volatile uint32_t *)APPLICATION_ADDR);
        /* 跳转到用户代码区 */
        jump2app();
    }
}

void install_firmware(void)
{
	uint32_t fileSize = (*(__IO uint32_t *)VERSION_INFO_FILESIZE_ADDR);
	uint32_t installedSize = 0;
	for(uint16_t i = 0; i <= fileSize / PAGE_SIZE; i++)
	{
		fmc_erase_page(APPLICATION_ADDR + PAGE_SIZE * i);
	}
	while(installedSize < fileSize)
	{
		uint32_t pageAddr = APPLICATION_ADDR + installedSize;
		uint8_t *data = (uint8_t *)(DOWNLOAD_ADDR + installedSize);
		fmc_program_page(pageAddr, data);
		installedSize += PAGE_SIZE;
	}
}

void fmc_erase_page(uint32_t pageAddr)
{
	fmc_unlock();
	fmc_flag_clear(FMC_FLAG_BANK0_END);
	fmc_flag_clear(FMC_FLAG_BANK0_WPERR);
	fmc_flag_clear(FMC_FLAG_BANK0_PGERR);

	fmc_page_erase(pageAddr);

	fmc_flag_clear(FMC_FLAG_BANK0_END);
	fmc_flag_clear(FMC_FLAG_BANK0_WPERR);
	fmc_flag_clear(FMC_FLAG_BANK0_PGERR);
	fmc_lock();
}

void fmc_program_page(uint32_t pageAddr, uint8_t *data)
{
	uint32_t addr = pageAddr;

	fmc_unlock();
	while(addr < pageAddr + PAGE_SIZE)
	{
		fmc_word_program(addr, *(uint32_t *)data);
		addr += 4;
		data += 4;
		fmc_flag_clear(FMC_FLAG_BANK0_END);
		fmc_flag_clear(FMC_FLAG_BANK0_WPERR);
		fmc_flag_clear(FMC_FLAG_BANK0_PGERR); 
	}
	fmc_lock();
}

void version_update_flag_reset(void)
{
	fmc_erase_page(VERSION_INFO_ADDR);
}
